package com.ihr360.insurance.service.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ihr360.authority.sharing.annonation.AccessControl;
import com.ihr360.commons.data.util.Pair;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.lang.DateUtils;
import com.ihr360.commons.lang.StringUtils;
import com.ihr360.commons.model.type.BenefitCategory;
import com.ihr360.commons.vo.PageData;
import com.ihr360.entity.BasicStaffInfoEntity;
import com.ihr360.entity.StaffBankSalaryEntity;
import com.ihr360.insurance.dao.StaffBenefitReposity;
import com.ihr360.insurance.entity.request.MemberPlanReq;
import com.ihr360.insurance.entity.request.StaffBenefitRequest;
import com.ihr360.insurance.entity.request.StaffLedgerRequest;
import com.ihr360.insurance.entity.vo.BenefitVo;
import com.ihr360.insurance.entity.vo.CompanyBenefitSimpleVo;
import com.ihr360.insurance.entity.vo.SingleStaffBenefitVo;
import com.ihr360.insurance.entity.vo.StaffBenefitEntity;
import com.ihr360.insurance.entity.vo.StaffBenefitVo;
import com.ihr360.insurance.model.dto.benefits.CompanyBenefit;
import com.ihr360.insurance.model.dto.staff.StaffBenefit;
import com.ihr360.insurance.model.dto.staff.embeddable.BenefitEntity;
import com.ihr360.insurance.service.CompanyBenefitService;
import com.ihr360.insurance.service.MonthlyLedgerService;
import com.ihr360.insurance.service.StaffBenefitService;
import com.ihr360.request.BasicStaffInfoReq;
import com.ihr360.rest.reposity.SimpleRestReposity;
import com.ihr360.rest.service.AbstractSimpleRestServiceImpl;
import com.ihr360.shared.basicstaffinfo.dao.SyncedStaffInfoRepository;
import com.ihr360.shared.basicstaffinfo.dto.SyncedBasicStaffInfo;
import com.ihr360.shared.basicstaffinfo.service.BasicStaffInfoService;
import com.ihr360.subscriber.StaffBenefitDataChangeSubscriber;
import com.ihr360.type.StaffStatus;
import com.ihr360.validation.ValidationUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import rx.Observable;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author Stone.Shi
 * @description
 * @date 2018-02-01 18:33:15.
 */
@Service
@AccessControl(disableAll = true)
public class StaffBenefitServiceImpl extends AbstractSimpleRestServiceImpl<StaffBenefit, Long> implements StaffBenefitService {

    private static final String ORDER_ASC = "ASC";

    @Autowired
    private StaffBenefitReposity reposity;

    @Autowired
    private CompanyBenefitService companyBenefitService;

    @Autowired
    private StaffBenefitDataChangeSubscriber dataChangeSubscriber;

    @Autowired
    private MonthlyLedgerService monthlyLedgerService;

    @Autowired
    private BasicStaffInfoService staffInfoService;

    @Autowired
    private SyncedStaffInfoRepository staffInfoRepository;

    @Override
    public SimpleRestReposity<StaffBenefit, Long> getSimpleRestReposity() {
        return reposity;
    }

    @Override
    public Integer getSocialInsuranceCount(Long companyBenefitId, List<String> authStaffIds) {
        return reposity.countAllBySiCompanyBenefitIdAndStaffIdIn(companyBenefitId, authStaffIds);
    }

    @Override
    public Integer getHouseFundCount(Long companyBenefitId, List<String> authStaffIds) {
        return reposity.countAllByHfCompanyBenefitIdAndStaffIdIn(companyBenefitId, authStaffIds);
    }

    @Override
    public int getRelatedStaffCount(String companyId, BenefitCategory category, List<String> authStaffIds) {
        if (CollectionUtils.isEmpty(authStaffIds)) {
            return 0;
        }
        List<CompanyBenefitSimpleVo> allCompanyBenefits = companyBenefitService.getCompanyBenefitVos(companyId);
        switch (category) {
            case SI:
                Set<Long> siIds = allCompanyBenefits.stream()
                        .filter(benefit -> benefit.getCategory() == BenefitCategory.SI)
                        .map(CompanyBenefitSimpleVo::getId).collect(Collectors.toSet());
                if (CollectionUtils.isEmpty(siIds)) {
                    return 0;
                }
                return reposity.countAllByCompanyIdAndSiCompanyBenefitIdInAndStaffIdIn(companyId, siIds, authStaffIds);
            case HF:
                Set<Long> hfIds = allCompanyBenefits.stream()
                        .filter(benefit -> benefit.getCategory() == BenefitCategory.HF)
                        .map(CompanyBenefitSimpleVo::getId).collect(Collectors.toSet());
                if (CollectionUtils.isEmpty(hfIds)) {
                    return 0;
                }
                return reposity.countAllByCompanyIdAndHfCompanyBenefitIdInAndStaffIdIn(companyId, hfIds, authStaffIds);
            default:
                return 0;
        }
    }

    @Override
    public int getUnRelatedStaffCount(String companyId, BenefitCategory category, List<String> authStaffIds) {
        if (CollectionUtils.isEmpty(authStaffIds)) {
            return 0;
        }
        List<CompanyBenefitSimpleVo> allCompanyBenefits = companyBenefitService.getCompanyBenefitVos(companyId);
        switch (category) {
            case SI:
                Set<Long> siIds = allCompanyBenefits.stream()
                        .filter(benefit -> benefit.getCategory() == BenefitCategory.SI)
                        .map(CompanyBenefitSimpleVo::getId).collect(Collectors.toSet());
                if (CollectionUtils.isEmpty(siIds)) {
                    return staffInfoService.getInServiceStaffCount(companyId, authStaffIds);
                }
                return staffInfoService.getInServiceStaffCount(companyId, authStaffIds)
                        - reposity.countAllByCompanyIdAndSiCompanyBenefitIdInAndStaffStaffStatusAndStaffIdIn(
                        companyId, siIds, StaffStatus.IN_SERVICE, authStaffIds);
            case HF:
                Set<Long> hfIds = allCompanyBenefits.stream()
                        .filter(benefit -> benefit.getCategory() == BenefitCategory.HF)
                        .map(CompanyBenefitSimpleVo::getId).collect(Collectors.toSet());
                if (CollectionUtils.isEmpty(hfIds)) {
                    return staffInfoService.getInServiceStaffCount(companyId, authStaffIds);
                }
                return staffInfoService.getInServiceStaffCount(companyId, authStaffIds)
                        - reposity.countAllByCompanyIdAndHfCompanyBenefitIdInAndStaffStaffStatusAndStaffIdIn(
                        companyId, hfIds, StaffStatus.IN_SERVICE, authStaffIds);
            default:
                return 0;
        }
    }

    @Override
    public List<StaffBenefitVo> getAllStaffBenefitInclude(String companyId, List<String> staffIds) {
        if (CollectionUtils.isEmpty(staffIds)) {
            return Collections.emptyList();
        }
        List<StaffBenefit> staffBenefits = reposity.findAllByCompanyIdAndStaffIdIn(companyId, staffIds);
        return getStaffBenefitVos(staffBenefits);
    }

    @Override
    public PageData<StaffBenefitVo> getUnrelatedStaffs(String companyId, List<String> authStaffIds, StaffBenefitRequest req) {
        if (CollectionUtils.isEmpty(authStaffIds)) {
            new PageData<>(0, 0, Collections.emptyList());
        }
        boolean containsStaffBenefitSearch = isContainsStaffBenefitSearch(req);
        if (containsStaffBenefitSearch) {
            Specification<StaffBenefit> unrelatedStaffsWhereClause = getUnrelatedStaffsWhereClause(companyId, authStaffIds, req);
            Page<StaffBenefit> page = reposity.findAll(unrelatedStaffsWhereClause,
                    getPageable(req.getPageNo(), req.getPageSize(), null));
            return new PageData<>(page.getTotalPages(), page.getTotalElements(), getStaffBenefitVos(page.getContent()));

        } else {
            Specification<StaffBenefit> relatedStaffsWhereClause = getRelatedStaffsWhereClause(companyId, authStaffIds, req);
            List<String> includeStaffIds = Lists.newArrayList(authStaffIds);
            if (relatedStaffsWhereClause != null) {
                List<StaffBenefit> excludeStaffs = reposity.findAll(relatedStaffsWhereClause);
                if (CollectionUtils.isNotEmpty(excludeStaffs)) {
                    includeStaffIds.removeAll(excludeStaffs.stream().map(benefit -> benefit.getStaff().getId()).collect(Collectors.toList()));
                }
            }

            PageData<BasicStaffInfoEntity> page = staffInfoService.getStaffsInclude(companyId, includeStaffIds,
                    new BasicStaffInfoReq(req.getStaffName(), req.getIdCardNo(),
                            req.getDepartmentIds(), req.getStaffStatus()),
                    getPageable(req.getPageNo(), req.getPageSize(), req.getSort()));

            return new PageData<>(page.getTotalPages(), page.getTotalElements(), generateStaffBenefitVos(companyId, page.getContent()));
        }
    }

    private Specification<StaffBenefit> getUnrelatedStaffsWhereClause(String companyId,
                                                                      List<String> authStaffIds,
                                                                      StaffBenefitRequest req) {
        return (root, q, cb) -> {

            Predicate predicate = cb.conjunction();
            predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.companyId).as(String.class), companyId));

            Expression<Long> siBenefitExp = getPath(root, StaffBenefit.Field.siCompanyBenefit_id).as(Long.class);
            Expression<Long> hfBenefitExp = getPath(root, StaffBenefit.Field.hfCompanyBenefit_id).as(Long.class);
            if (req.getCategory() == BenefitCategory.SI) {
                //如果指定companyBenefitId，则表示获取该方案id外的数据
                //如果未指定，则表示获取未关联方案（id==null）的数据
                if (req.getCompanyBenefitId() != null) {
                    predicate.getExpressions().add(cb.notEqual(siBenefitExp, req.getCompanyBenefitId()));
                } else {
                    List<CompanyBenefitSimpleVo> companyBenefitVos = companyBenefitService.getCompanyBenefitVos(companyId);
                    Set<Long> siIds = companyBenefitVos.stream()
                            .filter(benefit -> benefit.getCategory() == BenefitCategory.SI)
                            .map(CompanyBenefitSimpleVo::getId).collect(Collectors.toSet());
                    if (CollectionUtils.isNotEmpty(siIds)) {
                        predicate.getExpressions().add(siBenefitExp.in(siIds).not());
                    } else {
                        predicate.getExpressions().add(cb.isNull(siBenefitExp));
                    }
                }
            } else if (req.getCategory() == BenefitCategory.HF) {
                if (req.getCompanyBenefitId() != null) {
                    predicate.getExpressions().add(cb.notEqual(hfBenefitExp, req.getCompanyBenefitId()));
                } else {
                    List<CompanyBenefitSimpleVo> companyBenefitVos = companyBenefitService.getCompanyBenefitVos(companyId);
                    Set<Long> hfIds = companyBenefitVos.stream()
                            .filter(benefit -> benefit.getCategory() == BenefitCategory.HF)
                            .map(CompanyBenefitSimpleVo::getId).collect(Collectors.toSet());
                    if (CollectionUtils.isNotEmpty(hfIds)) {
                        predicate.getExpressions().add(hfBenefitExp.in(hfIds).not());
                    } else {
                        predicate.getExpressions().add(cb.isNull(hfBenefitExp));
                    }
                }
            } else {
                ValidationUtils.reject(null, "缺少社保公积金类别参数");
            }

            if (CollectionUtils.isNotEmpty(req.getSiCompanyBenefitIds())) {
                predicate.getExpressions().add(siBenefitExp
                        .in(req.getSiCompanyBenefitIds()));
            }
            if (req.getSiStartOn() != null) {
                predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.socialInsurance_startOn).as(Date.class),
                        DateUtils.dateFormatYMD(req.getSiStartOn())));
            }
            if (req.getSiEndOn() != null) {
                predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.socialInsurance_endOn).as(Date.class),
                        DateUtils.dateFormatYMD(req.getSiEndOn())));
            }

            if (CollectionUtils.isNotEmpty(req.getHfCompanyBenefitIds())) {
                predicate.getExpressions().add(hfBenefitExp
                        .in(req.getHfCompanyBenefitIds()));
            }
            if (req.getHfStartOn() != null) {
                predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.houseFund_startOn).as(Date.class),
                        DateUtils.dateFormatYMD(req.getHfStartOn())));
            }
            if (req.getHfEndOn() != null) {
                predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.houseFund_endOn).as(Date.class),
                        DateUtils.dateFormatYMD(req.getHfEndOn())));
            }
            Join<StaffBenefit, SyncedBasicStaffInfo> staff = root.join(StaffBenefit.Field.staff, JoinType.LEFT);
            predicate.getExpressions().add(staff.get(SyncedBasicStaffInfo.Field.id).as(String.class).in(authStaffIds));

            if (req.getStaffStatus() != null) {
                predicate.getExpressions().add(
                        cb.equal(staff.get(SyncedBasicStaffInfo.Field.staffStatus).as(String.class), req.getStaffStatus().name()));
            }

            if (StringUtils.isNotBlank(req.getStaffName())) {
                predicate.getExpressions().add(
                        cb.like(staff.get(SyncedBasicStaffInfo.Field.staffName).as(String.class), "%" + req.getStaffName() + "%"));
            }

            if (StringUtils.isNotBlank(req.getIdCardNo())) {
                predicate.getExpressions().add(
                        cb.like(staff.get(SyncedBasicStaffInfo.Field.idCardNo).as(String.class), req.getIdCardNo() + "%"));
            }

            if (CollectionUtils.isNotEmpty(req.getDepartmentIds())) {
                predicate.getExpressions().add(
                        staff.get(SyncedBasicStaffInfo.Field.departmentId).as(Long.class).in(req.getDepartmentIds()));
            }

            q.where(predicate);
            //仅支持员工的基本信息数据排序
            Order order = getOrder(req.getSort(), staff, cb);
            if (order != null) {
                q.orderBy(order);
            }

            return q.getRestriction();
        };
    }

    private Specification<StaffBenefit> getRelatedStaffsWhereClause(String companyId,
                                                                    List<String> authStaffIds,
                                                                    StaffBenefitRequest req) {
        Set<Long> benefitIds = Sets.newHashSet();
        if (req.getCompanyBenefitId() == null) {
            List<CompanyBenefitSimpleVo> companyBenefitVos = companyBenefitService.getCompanyBenefitVos(companyId);
            if (req.getCategory() == BenefitCategory.SI) {
                benefitIds = companyBenefitVos.stream()
                        .filter(benefit -> benefit.getCategory() == BenefitCategory.SI)
                        .map(CompanyBenefitSimpleVo::getId).collect(Collectors.toSet());
                if (CollectionUtils.isEmpty(benefitIds)) {
                    return null;
                }
            } else if (req.getCategory() == BenefitCategory.HF) {
                benefitIds = companyBenefitVos.stream()
                        .filter(benefit -> benefit.getCategory() == BenefitCategory.HF)
                        .map(CompanyBenefitSimpleVo::getId).collect(Collectors.toSet());
                if (CollectionUtils.isEmpty(benefitIds)) {
                    return null;
                }
            } else {
                return null;
            }
        }

        Set<Long> finalBenefitIds = benefitIds;
        return (root, q, cb) -> {

            Predicate predicate = cb.conjunction();
            predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.companyId).as(String.class), companyId));
            predicate.getExpressions().add(getPath(root, StaffBenefit.Field.staff_id).as(String.class).in(authStaffIds));

            if (req.getCategory() == BenefitCategory.SI) {
                //如果指定companyBenefitId，则表示获取该方案id内的数据
                //如果未指定，则表示获取已关联方案的数据
                Expression<Long> siBenefitExp = getPath(root, StaffBenefit.Field.siCompanyBenefit_id).as(Long.class);
                if (req.getCompanyBenefitId() != null) {
                    predicate.getExpressions().add(cb.equal(siBenefitExp, req.getCompanyBenefitId()));
                } else {
                    predicate.getExpressions().add(siBenefitExp.in(finalBenefitIds));
                }
            } else if (req.getCategory() == BenefitCategory.HF) {
                Expression<Long> hfBenefitExp = getPath(root, StaffBenefit.Field.hfCompanyBenefit_id).as(Long.class);
                if (req.getCompanyBenefitId() != null) {
                    predicate.getExpressions().add(cb.equal(hfBenefitExp, req.getCompanyBenefitId()));
                } else {
                    predicate.getExpressions().add(hfBenefitExp.in(finalBenefitIds));
                }
            } else {
                ValidationUtils.reject(null, "缺少社保公积金类别参数");
            }

            return predicate;
        };
    }

    @Override
    public PageData<StaffBenefitVo> getList(String companyId, List<String> authStaffIds, StaffBenefitRequest req) {
        if (CollectionUtils.isEmpty(authStaffIds)) {
            new PageData<>(0, 0, Collections.emptyList());
        }
        boolean containsStaffBenefitSearch = isContainsStaffBenefitSearch(req);
        if (containsStaffBenefitSearch) {
            Specification<StaffBenefit> withSearch = getStaffBenefitWhereClauseWithSearch(companyId, authStaffIds, req);
            Page<StaffBenefit> page = reposity.findAll(withSearch,
                    getPageable(req.getPageNo(), req.getPageSize(), null));
            return new PageData<>(page.getTotalPages(), page.getTotalElements(), getStaffBenefitVos(page.getContent()));

        } else {
            PageData<BasicStaffInfoEntity> page = staffInfoService.getStaffsInclude(companyId, authStaffIds,
                    new BasicStaffInfoReq(req.getStaffName(), req.getIdCardNo(),
                            req.getDepartmentIds(), req.getStaffStatus()),
                    getPageable(req.getPageNo(), req.getPageSize(), req.getSort()));

            return new PageData<>(page.getTotalPages(), page.getTotalElements(), generateStaffBenefitVos(companyId, page.getContent()));
        }
    }

    private Specification<StaffBenefit> getStaffBenefitWhereClauseWithSearch(String companyId,
                                                                             List<String> authStaffIds,
                                                                             StaffBenefitRequest req) {
        return (root, q, cb) -> {

            Predicate predicate = cb.conjunction();
            predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.companyId).as(String.class), companyId));

            if (CollectionUtils.isNotEmpty(req.getSiCompanyBenefitIds())) {
                predicate.getExpressions().add(getPath(root, StaffBenefit.Field.siCompanyBenefit_id).as(Long.class)
                        .in(req.getSiCompanyBenefitIds()));
            }
            if (req.getSiStartOn() != null) {
                predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.socialInsurance_startOn).as(Date.class),
                        DateUtils.dateFormatYMD(req.getSiStartOn())));
            }
            if (req.getSiEndOn() != null) {
                predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.socialInsurance_endOn).as(Date.class),
                        DateUtils.dateFormatYMD(req.getSiEndOn())));
            }

            if (CollectionUtils.isNotEmpty(req.getHfCompanyBenefitIds())) {
                predicate.getExpressions().add(getPath(root, StaffBenefit.Field.hfCompanyBenefit_id).as(Long.class)
                        .in(req.getHfCompanyBenefitIds()));
            }
            if (req.getHfStartOn() != null) {
                predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.houseFund_startOn).as(Date.class),
                        DateUtils.dateFormatYMD(req.getHfStartOn())));
            }
            if (req.getHfEndOn() != null) {
                predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.houseFund_endOn).as(Date.class),
                        DateUtils.dateFormatYMD(req.getHfEndOn())));
            }
            Join<StaffBenefit, SyncedBasicStaffInfo> staff = root.join(StaffBenefit.Field.staff, JoinType.LEFT);
            predicate.getExpressions().add(staff.get(SyncedBasicStaffInfo.Field.id).as(String.class).in(authStaffIds));

            if (req.getStaffStatus() != null) {
                predicate.getExpressions().add(
                        cb.equal(staff.get(SyncedBasicStaffInfo.Field.staffStatus).as(String.class), req.getStaffStatus().name()));
            }

            if (StringUtils.isNotBlank(req.getStaffName())) {
                predicate.getExpressions().add(
                        cb.like(staff.get(SyncedBasicStaffInfo.Field.staffName).as(String.class), "%" + req.getStaffName() + "%"));
            }

            if (StringUtils.isNotBlank(req.getIdCardNo())) {
                predicate.getExpressions().add(
                        cb.like(staff.get(SyncedBasicStaffInfo.Field.idCardNo).as(String.class), req.getIdCardNo() + "%"));
            }

            if (CollectionUtils.isNotEmpty(req.getDepartmentIds())) {
                predicate.getExpressions().add(
                        staff.get(SyncedBasicStaffInfo.Field.departmentId).as(Long.class).in(req.getDepartmentIds()));
            }

            q.where(predicate);
            //仅支持员工的基本信息数据排序
            Order order = getOrder(req.getSort(), staff, cb);
            if (order != null) {
                q.orderBy(order);
            }

            return q.getRestriction();
        };
    }


    private boolean isContainsStaffBenefitSearch(StaffBenefitRequest req) {
        return CollectionUtils.isNotEmpty(req.getSiCompanyBenefitIds())
                || req.getSiStartOn() != null
                || req.getSiEndOn() != null
                || CollectionUtils.isNotEmpty(req.getHfCompanyBenefitIds())
                || req.getHfStartOn() != null
                || req.getHfEndOn() != null;
    }

    @Override
    public PageData<StaffBenefitVo> getCanAddToLedgerStaffs(String companyId, String userId,
                                                            List<String> authStaffIds, StaffLedgerRequest req) {
        if (CollectionUtils.isEmpty(authStaffIds)) {
            return new PageData<>(0, 0, Collections.emptyList());
        }

        List<CompanyBenefitSimpleVo> allCompanyBenefits = companyBenefitService.getCompanyBenefitVos(companyId);
        Set<Long> siIds = Sets.newHashSet();
        Set<Long> hfIds = Sets.newHashSet();
        allCompanyBenefits.forEach(benefit -> {
            if (benefit.getCategory() == BenefitCategory.SI) {
                siIds.add(benefit.getId());
            } else if (benefit.getCategory() == BenefitCategory.HF) {
                hfIds.add(benefit.getId());
            }
        });
        //不存在任何有效社保和公积金方案
        if (CollectionUtils.isEmpty(siIds) && CollectionUtils.isEmpty(hfIds)) {
            return new PageData<>(0, 0, Collections.emptyList());
        }

        Specification<StaffBenefit> whereClause = getCanAddToLedgerStaffWhereClause(companyId, userId, authStaffIds, req, siIds, hfIds);
        Pageable pageable = getPageable(req.getPageNo(), req.getPageSize(), null);
        Page<StaffBenefit> benefits = reposity.findAll(whereClause, pageable);
        List<StaffBenefitVo> staffBenefitVos = getStaffBenefitVos(benefits.getContent());
        return new PageData<>(benefits.getTotalPages(), benefits.getTotalElements(), staffBenefitVos);
    }

    private Specification<StaffBenefit> getCanAddToLedgerStaffWhereClause(String companyId,
                                                                          String userId,
                                                                          List<String> authStaffIds,
                                                                          StaffLedgerRequest req,
                                                                          Set<Long> siIds,
                                                                          Set<Long> hfIds) {
        return (root, q, cb) -> {
            Predicate predicate = cb.conjunction();
            predicate.getExpressions().add(cb.equal(root.get(StaffBenefit.Field.companyId).as(String.class), companyId));
            predicate.getExpressions().add(getPath(root, StaffBenefit.Field.staff_id).as(String.class).in(authStaffIds));

            Integer ledgerYear = req.getLedgerYear();
            Integer ledgerMonth = req.getLedgerMonth();
            validateLedgerDate(ledgerYear, ledgerMonth);

            Expression<Long> siBenefitExp = getPath(root, StaffBenefit.Field.siCompanyBenefit_id).as(Long.class);
            if (CollectionUtils.isNotEmpty(req.getSiCompanyBenefitIds())) {
                predicate.getExpressions().add(siBenefitExp.in(req.getSiCompanyBenefitIds()));
            }
            Expression<Long> hfBenefitExp = getPath(root, StaffBenefit.Field.hfCompanyBenefit_id).as(Long.class);
            if (CollectionUtils.isNotEmpty(req.getHfCompanyBenefitIds())) {
                predicate.getExpressions().add(hfBenefitExp.in(req.getHfCompanyBenefitIds()));
            }


            //////////////////限制员工的方案至少有一个有效////////////////////////////
            Date ledgerMonthly = getYearMonthDate(ledgerYear, ledgerMonth);
            Predicate siAnd = null;
            if (CollectionUtils.isNotEmpty(siIds)) {
                Expression<Date> siEndOn = getPath(root, StaffBenefit.Field.socialInsurance_endOn).as(Date.class);
                siAnd = cb.conjunction();
                siAnd.getExpressions().addAll(Lists.newArrayList(
                        siBenefitExp.in(siIds),
                        cb.lessThanOrEqualTo(getPath(root, StaffBenefit.Field.socialInsurance_startOn).as(Date.class), ledgerMonthly),
                        cb.or(cb.isNull(siEndOn), cb.greaterThanOrEqualTo(siEndOn, ledgerMonthly))
                ));
            }

            Predicate hfAnd = null;
            if (CollectionUtils.isNotEmpty(hfIds)) {
                Expression<Date> hfEndOn = getPath(root, StaffBenefit.Field.houseFund_endOn).as(Date.class);
                hfAnd = cb.conjunction();
                hfAnd.getExpressions().addAll(Lists.newArrayList(
                        hfBenefitExp.in(hfIds),
                        cb.lessThanOrEqualTo(getPath(root, StaffBenefit.Field.houseFund_startOn).as(Date.class), ledgerMonthly),
                        cb.or(cb.isNull(hfEndOn), cb.greaterThanOrEqualTo(hfEndOn, ledgerMonthly))
                ));
            }

            List<String> excludeStaffIds = monthlyLedgerService.getStaffIds(companyId, userId, ledgerYear, ledgerMonth);
            Join<StaffBenefit, SyncedBasicStaffInfo> staff = root.join(StaffBenefit.Field.staff, JoinType.LEFT);

            if (CollectionUtils.isNotEmpty(excludeStaffIds)) {
                Predicate in = staff.get(SyncedBasicStaffInfo.Field.id).as(String.class).in(excludeStaffIds);
                Predicate notIn = cb.not(in);
                predicate.getExpressions().add(notIn);
            }

            if (StringUtils.isNotBlank(req.getStaffName())) {
                predicate.getExpressions().add(cb.like(staff.get(SyncedBasicStaffInfo.Field.staffName).as(String.class),
                        "%" + req.getStaffName() + "%"));
            }
            if (StringUtils.isNotBlank(req.getIdCardNo())) {
                predicate.getExpressions().add(cb.like(staff.get(SyncedBasicStaffInfo.Field.idCardNo).as(String.class),
                        req.getIdCardNo() + "%"));
            }
            if (CollectionUtils.isNotEmpty(req.getDepartmentIds())) {
                predicate.getExpressions().add(
                        staff.get(SyncedBasicStaffInfo.Field.departmentId).as(Long.class).in(req.getDepartmentIds()));
            }

            if (siAnd == null) {
                q.where(cb.and(predicate, hfAnd));
            } else if (hfAnd == null) {
                q.where(cb.and(predicate, siAnd));
            } else {
                q.where(cb.and(predicate, cb.or(siAnd, hfAnd)));
            }

            //仅支持员工的基本信息数据排序
            Order order = getOrder(req.getSort(), staff, cb);
            if (order != null) {
                q.orderBy(order);
            }

            return q.getRestriction();
        };
    }

    private void validateLedgerDate(Integer ledgerYear, Integer ledgerMonth) {
        ValidationUtils.rejectIfEmpty(ledgerYear, "", "台账年份不能为空");
        ValidationUtils.rejectIfEmpty(ledgerMonth, "", "台账月份不能为空");
        ValidationUtils.rejectNotInRange(ledgerMonth, 1, 12, "", "台账月份无效");
    }

    private Date getYearMonthDate(int ledgerYear, int ledgerMonth) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, ledgerYear);
        calendar.set(Calendar.MONTH, ledgerMonth - 1);
        calendar.set(Calendar.DATE, 1);
        return DateUtils.dateFormatYMD(calendar.getTime());
    }

    /**
     * 仅处理单属性字段排序
     *
     * @param sort
     * @param root
     * @param cb
     */
    private Order getOrder(String sort, Path root, CriteriaBuilder cb) {
        if (StringUtils.isBlank(sort)) {
            return null;
        }
        String[] o = sort.split(",");
        if (o.length == 1) {
            return cb.asc(getPath(root, o[0]));
        } else if (o.length == 2) {
            if (ORDER_ASC.equalsIgnoreCase(o[1])) {
                return cb.asc(getPath(root, o[0]));
            } else {
                return cb.desc(getPath(root, o[0]));
            }
        }
        return null;
    }

    private Pageable getPageable(Integer pageNo, Integer pageSize, String sortString) {
        int page = pageNo == null ? 0 : pageNo;
        if (page > 0) {
            page--;
        }
        Sort sort = null;
        if (StringUtils.isNotBlank(sortString)) {
            String[] o = sortString.split(",");
            Sort.Order order;
            if (o.length == 2) {
                order = new Sort.Order(Sort.Direction.fromString(o[1]), o[0]);
                sort = new Sort(order);
            } else if (o.length == 1) {
                order = new Sort.Order(Sort.Direction.ASC, o[0]);
                sort = new Sort(order);
            }
        }
        return new PageRequest(page, pageSize == null ? 0 : pageSize, sort);
    }

    private Path<StaffBenefit> getPath(Path<StaffBenefit> root, String attributeName) {
        String[] attributes = attributeName.split("\\.");
        Path<StaffBenefit> path = root;
        for (String attribute : attributes) {
            path = path.get(attribute);
        }
        return path;
    }

    private List<StaffBenefitVo> getStaffBenefitVos(List<StaffBenefit> benefits) {
        if (CollectionUtils.isEmpty(benefits)) {
            return Collections.emptyList();
        }
        List<StaffBenefitVo> result = Lists.newArrayListWithCapacity(benefits.size());
        benefits.forEach(item -> result.add(
                new StaffBenefitVo(item.getId(), item.getStaffInfoVo(), item.getSocialInsuranceVo(), item.getHouseFundVo())));
        return result;
    }

    private List<StaffBenefitVo> generateStaffBenefitVos(String companyId, List<BasicStaffInfoEntity> staffInfoEntities) {
        if (CollectionUtils.isEmpty(staffInfoEntities)) {
            return Collections.emptyList();
        }
        List<String> includeStaffIds = staffInfoEntities.stream().map(BasicStaffInfoEntity::getId).collect(Collectors.toList());
        List<StaffBenefit> staffBenefits = reposity.findAllByCompanyIdAndStaffIdIn(companyId, includeStaffIds);

        Map<String, StaffBenefitVo> staffIdVoMap = Maps.newHashMap();
        if (CollectionUtils.isNotEmpty(staffBenefits)) {
            List<StaffBenefitVo> includeStaffBenefitVos = getStaffBenefitVos(staffBenefits);
            includeStaffBenefitVos.forEach(item -> staffIdVoMap.put(item.getStaff().getId(), item));
        }

        List<StaffBenefitVo> result = Lists.newArrayListWithCapacity(staffInfoEntities.size());
        staffInfoEntities.forEach(staff -> {
            StaffBenefitVo vo;
            if (staffIdVoMap.containsKey(staff.getId())) {
                vo = staffIdVoMap.get(staff.getId());
            } else {
                vo = new StaffBenefitVo(staff);
            }
            result.add(vo);
        });
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveBenefits(String companyId, List<StaffBenefitVo> benefitVos, BenefitCategory category, List<String> authStaffIds) {
        if (CollectionUtils.isEmpty(benefitVos)
                || CollectionUtils.isEmpty(authStaffIds)) {
            return;
        }
        validate(benefitVos, category);
        doSave(companyId, benefitVos, authStaffIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveSingleBenefits(String companyId, List<SingleStaffBenefitVo> singleBenefitVos, BenefitCategory category, List<String> authStaffIds) {
        if (CollectionUtils.isEmpty(singleBenefitVos)
                || CollectionUtils.isEmpty(authStaffIds)) {
            return;
        }
        List<StaffBenefitVo> staffBenefitVos = Lists.newArrayListWithCapacity(singleBenefitVos.size());
        singleBenefitVos.forEach(item -> {
            StaffBenefitVo vo = new StaffBenefitVo();
            vo.setId(item.getId());
            vo.setStaff(item.getStaff());
            if (category == BenefitCategory.SI) {
                vo.setSocialInsurance(item.getBenefit());
            } else {
                vo.setHouseFund(item.getBenefit());
            }
            staffBenefitVos.add(vo);
        });
        saveBenefits(companyId, staffBenefitVos, category, authStaffIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSingleBenefits(String companyId, List<SingleStaffBenefitVo> singleBenefitVos, BenefitCategory category, List<String> authStaffIds) {
        if (CollectionUtils.isEmpty(singleBenefitVos)
                || CollectionUtils.isEmpty(authStaffIds)) {
            return;
        }
        List<StaffBenefitVo> staffBenefitVos = Lists.newArrayListWithCapacity(singleBenefitVos.size());
        singleBenefitVos.forEach(item -> {
            ValidationUtils.rejectIfAllNull("", "要删除的数据不存在", item.getId(), item.getBenefit());
            BenefitVo benefit = item.getBenefit();
            benefit.setCompanyBenefitId(null);

            StaffBenefitVo vo = new StaffBenefitVo();
            vo.setId(item.getId());
            if (category == BenefitCategory.SI) {
                vo.setSocialInsurance(benefit);
            } else {
                vo.setHouseFund(benefit);
            }
            staffBenefitVos.add(vo);
        });
        doSave(companyId, staffBenefitVos, authStaffIds);
    }

    /**
     * 保存社保公积金数据；
     *
     * @param benefitVos
     * @param authStaffIds
     */
    private void doSave(String companyId, List<StaffBenefitVo> benefitVos, List<String> authStaffIds) {

        //数据权限验证
        benefitVos.removeIf(vo -> !authStaffIds.contains(vo.getStaff().getId()));
        if (CollectionUtils.isEmpty(benefitVos)) {
            return;
        }

        Map<Long, StaffBenefit> existedBenefitDtoMap = getExistedBenefitDtoMap(benefitVos);

        Set<Long> affectedSiCompanyBenefitIds = Sets.newHashSet();
        Set<Long> affectedHfCompanyBenefitIds = Sets.newHashSet();

        List<StaffBenefit> deletedDtos = Lists.newArrayList();
        List<StaffBenefit> savedDtos = Lists.newArrayList();

        benefitVos.forEach(vo -> {
            Long id = vo.getId();
            StaffBenefit dto;
            if (id != null && existedBenefitDtoMap.containsKey(id)) {
                dto = existedBenefitDtoMap.get(id);
            } else {
                dto = new StaffBenefit();
                SyncedBasicStaffInfo staff = new SyncedBasicStaffInfo();
                staff.setId(vo.getStaff().getId());
                dto.setStaff(staff);
            }

            BenefitVo socialInsurance = vo.getSocialInsurance();
            if (socialInsurance != null) {
                BenefitEntity siEntity = new BenefitEntity();
                BeanUtils.copyProperties(socialInsurance, siEntity);
                dto.setSocialInsurance(siEntity);

                Long companyBenefitId = socialInsurance.getCompanyBenefitId();
                if (companyBenefitId == null) {
                    dto.setSiCompanyBenefit(null);
                    dto.setTmpSiBenefitId(null);
                } else {
                    CompanyBenefit siCb = new CompanyBenefit();
                    siCb.setId(companyBenefitId);
                    dto.setSiCompanyBenefit(siCb);
                    dto.setTmpSiBenefitId(companyBenefitId.toString());
                }
            }

            BenefitVo houseFund = vo.getHouseFund();
            if (houseFund != null) {
                BenefitEntity hfEntity = new BenefitEntity();
                BeanUtils.copyProperties(houseFund, hfEntity);
                dto.setHouseFund(hfEntity);

                Long companyBenefitId = houseFund.getCompanyBenefitId();
                if (companyBenefitId == null) {
                    dto.setHfCompanyBenefit(null);
                } else {
                    CompanyBenefit hfCb = new CompanyBenefit();
                    hfCb.setId(companyBenefitId);
                    dto.setHfCompanyBenefit(hfCb);
                }
            }
            //社保和公积金数据均不存在，则数据可删除
            if (dto.getSocialInsurance() == null && dto.getSiCompanyBenefit() == null
                    && dto.getHouseFund() == null && dto.getHfCompanyBenefit() == null) {
                deletedDtos.add(dto);
            } else {
                savedDtos.add(dto);
            }
            collectAffectedIds(affectedSiCompanyBenefitIds, socialInsurance);
            collectAffectedIds(affectedHfCompanyBenefitIds, houseFund);
        });
        if (CollectionUtils.isNotEmpty(deletedDtos)) {
            reposity.delete(deletedDtos);
        }
        if (CollectionUtils.isNotEmpty(savedDtos)) {
            reposity.save(savedDtos);
        }
        if (CollectionUtils.isNotEmpty(affectedSiCompanyBenefitIds)) {
            Observable.just(Pair.of(authStaffIds, affectedSiCompanyBenefitIds)).subscribe(dataChangeSubscriber.syncSocialInsuranceCount);
        }
        if (CollectionUtils.isNotEmpty(affectedHfCompanyBenefitIds)) {
            Observable.just(Pair.of(authStaffIds, affectedHfCompanyBenefitIds)).subscribe(dataChangeSubscriber.syncHouseFundCount);
        }
    }

    private Map<Long, StaffBenefit> getExistedBenefitDtoMap(List<StaffBenefitVo> benefitVos) {
        List<Long> existedIds = Lists.newArrayList();
        benefitVos.forEach(item -> {
            if (item.getId() != null) {
                existedIds.add(item.getId());
            }
        });
        if (CollectionUtils.isEmpty(existedIds)) {
            return Maps.newHashMap();
        }
        Iterable<StaffBenefit> existedDtos = reposity.findAll(existedIds);
        ArrayList<StaffBenefit> staffBenefits = Lists.newArrayList(existedDtos);
        return staffBenefits.stream().collect(
                Collectors.toMap(StaffBenefit::getId, self -> self));

    }

    /**
     * 获取受方案变更所影响的数据
     *
     * @param affectedCompanyBenefitIds
     * @param benefitVo
     */
    private void collectAffectedIds(Set<Long> affectedCompanyBenefitIds, BenefitVo benefitVo) {
        if (benefitVo == null) {
            return;
        }
        Long companyBenefitId = benefitVo.getCompanyBenefitId();
        Long originalCompanyBenefitId = benefitVo.getOriginalCompanyBenefitId();
        if (companyBenefitId != null && !companyBenefitId.equals(originalCompanyBenefitId)) {
            affectedCompanyBenefitIds.add(companyBenefitId);
            if (originalCompanyBenefitId != null) {
                affectedCompanyBenefitIds.add(originalCompanyBenefitId);
            }
        }

        if (originalCompanyBenefitId != null && !originalCompanyBenefitId.equals(companyBenefitId)) {
            affectedCompanyBenefitIds.add(originalCompanyBenefitId);
            if (companyBenefitId != null) {
                affectedCompanyBenefitIds.add(originalCompanyBenefitId);
            }
        }
    }

    /**
     * 校验规则：
     * 社保类型时，社保方案必须有效；
     * 公积金类型时，公积金方案必须有效
     *
     * @param benefitVos
     * @param category
     */
    private void validate(List<StaffBenefitVo> benefitVos, BenefitCategory category) {
        if (CollectionUtils.isEmpty(benefitVos)) {
            return;
        }
        benefitVos.forEach(item -> {
            if (category == BenefitCategory.SI) {
                ValidationUtils.rejectIfAnyNull("", "社保方案不能为空", item.getSocialInsurance());
                ValidationUtils.rejectIfAnyNull("", "社保方案不能为空", item.getSocialInsurance().getCompanyBenefitId());
            } else if (category == BenefitCategory.HF) {
                ValidationUtils.rejectIfAnyNull("", "公积金方案不能为空", item.getHouseFund());
                ValidationUtils.rejectIfAnyNull("", "公积金方案不能为空", item.getHouseFund().getCompanyBenefitId());
            }
        });
    }

    @Override
    public PageData<StaffBenefitVo> getIncreaseMember(String companyId, List<String> authStaffIds, MemberPlanReq req) {
        if (CollectionUtils.isEmpty(authStaffIds)) {
            new PageData<>(0, 0, Collections.emptyList());
        }

        List<CompanyBenefitSimpleVo> allCompanyBenefits = companyBenefitService.getCompanyBenefitVos(companyId);
        Set<Long> siIds = Sets.newHashSet();
        Set<Long> hfIds = Sets.newHashSet();
        allCompanyBenefits.forEach(benefit -> {
            if (benefit.getCategory() == BenefitCategory.SI) {
                siIds.add(benefit.getId());
            } else if (benefit.getCategory() == BenefitCategory.HF) {
                hfIds.add(benefit.getId());
            }
        });
        //不存在任何有效社保和公积金方案
        if (CollectionUtils.isEmpty(siIds) && CollectionUtils.isEmpty(hfIds)) {
            return new PageData<>(0, 0, Collections.emptyList());
        }

        Specification<StaffBenefit> withSearch = getMemberWhereClause(companyId, authStaffIds, req, true, siIds, hfIds);
        Page<StaffBenefit> page = reposity.findAll(withSearch,
                getPageable(req.getPageNo(), req.getPageSize(), null));
        return new PageData<>(page.getTotalPages(), page.getTotalElements(), getStaffBenefitVos(page.getContent()));
    }

    @Override
    public PageData<StaffBenefitVo> getDecreaseMember(String companyId, List<String> authStaffIds, MemberPlanReq req) {
        if (CollectionUtils.isEmpty(authStaffIds)) {
            new PageData<>(0, 0, Collections.emptyList());
        }

        List<CompanyBenefitSimpleVo> allCompanyBenefits = companyBenefitService.getCompanyBenefitVos(companyId);
        Set<Long> siIds = Sets.newHashSet();
        Set<Long> hfIds = Sets.newHashSet();
        allCompanyBenefits.forEach(benefit -> {
            if (benefit.getCategory() == BenefitCategory.SI) {
                siIds.add(benefit.getId());
            } else if (benefit.getCategory() == BenefitCategory.HF) {
                hfIds.add(benefit.getId());
            }
        });
        //不存在任何有效社保和公积金方案
        if (CollectionUtils.isEmpty(siIds) && CollectionUtils.isEmpty(hfIds)) {
            return new PageData<>(0, 0, Collections.emptyList());
        }

        Specification<StaffBenefit> withSearch = getMemberWhereClause(companyId, authStaffIds, req, false, siIds, hfIds);
        Page<StaffBenefit> page = reposity.findAll(withSearch,
                getPageable(req.getPageNo(), req.getPageSize(), null));
        return new PageData<>(page.getTotalPages(), page.getTotalElements(), getStaffBenefitVos(page.getContent()));
    }

    /**
     * 增减员计划人员仅关注起始缴纳月
     *
     * @param companyId
     * @param authStaffIds
     * @param req
     * @param isIncrease
     * @param siIds
     *@param hfIds @return
     */
    private Specification<StaffBenefit> getMemberWhereClause(String companyId,
                                                             List<String> authStaffIds,
                                                             MemberPlanReq req,
                                                             boolean isIncrease,
                                                             Set<Long> siIds,
                                                             Set<Long> hfIds) {
        return (root, q, cb) -> {

            Predicate predicate = cb.conjunction();
            predicate.getExpressions().add(cb.equal(getPath(root, StaffBenefit.Field.companyId).as(String.class), companyId));
            predicate.getExpressions().add(getPath(root, StaffBenefit.Field.staff_id).as(String.class).in(authStaffIds));

            Expression<Long> siBenefitExp = getPath(root, StaffBenefit.Field.siCompanyBenefit_id).as(Long.class);
            if (CollectionUtils.isNotEmpty(req.getSiCompanyBenefitIds())) {
                predicate.getExpressions().add(siBenefitExp.in(req.getSiCompanyBenefitIds()));
            }
            Expression<Long> hfBenefitExp = getPath(root, StaffBenefit.Field.hfCompanyBenefit_id).as(Long.class);
            if (CollectionUtils.isNotEmpty(req.getHfCompanyBenefitIds())) {
                predicate.getExpressions().add(hfBenefitExp.in(req.getHfCompanyBenefitIds()));
            }

            //////////////////限制员工的方案至少有一个有效////////////////////////////
            Date yearMonthDate = getYearMonthDate(req.getYear(), req.getMonth());
            Predicate siAnd = null;
            if (CollectionUtils.isNotEmpty(siIds)) {
                siAnd = cb.conjunction();
                siAnd.getExpressions().addAll(Lists.newArrayList(
                        siBenefitExp.in(siIds),
                        isIncrease ? cb.equal(getPath(root, StaffBenefit.Field.socialInsurance_startOn).as(Date.class), yearMonthDate)
                                : cb.equal(getPath(root, StaffBenefit.Field.socialInsurance_endOn).as(Date.class), yearMonthDate))
                );
            }

            Predicate hfAnd = null;
            if (CollectionUtils.isNotEmpty(hfIds)) {
                hfAnd = cb.conjunction();
                hfAnd.getExpressions().addAll(Lists.newArrayList(
                        hfBenefitExp.in(hfIds),
                        isIncrease ? cb.equal(getPath(root, StaffBenefit.Field.houseFund_startOn).as(Date.class), yearMonthDate)
                                : cb.equal(getPath(root, StaffBenefit.Field.houseFund_endOn).as(Date.class), yearMonthDate))
                );
            }

            Join<StaffBenefit, SyncedBasicStaffInfo> staff = root.join(StaffBenefit.Field.staff, JoinType.LEFT);

            if (StringUtils.isNotBlank(req.getStaffName())) {
                predicate.getExpressions().add(
                        cb.like(staff.get(SyncedBasicStaffInfo.Field.staffName).as(String.class), "%" + req.getStaffName() + "%"));
            }

            if (StringUtils.isNotBlank(req.getIdCardNo())) {
                predicate.getExpressions().add(
                        cb.like(staff.get(SyncedBasicStaffInfo.Field.idCardNo).as(String.class), req.getIdCardNo() + "%"));
            }

            if (CollectionUtils.isNotEmpty(req.getDepartmentIds())) {
                predicate.getExpressions().add(
                        staff.get(SyncedBasicStaffInfo.Field.departmentId).as(Long.class).in(req.getDepartmentIds()));
            }

            if (siAnd == null) {
                q.where(cb.and(predicate, hfAnd));
            } else if (hfAnd == null) {
                q.where(cb.and(predicate, siAnd));
            } else {
                q.where(cb.and(predicate, cb.or(siAnd, hfAnd)));
            }

            //仅支持员工的基本信息数据排序
            Order order = getOrder(req.getSort(), staff, cb);
            if (order != null) {
                q.orderBy(order);
            }

            return q.getRestriction();
        };
    }

    //***************************************花名册编辑社保公积金数据相关功能********************************************/

    @Override
    public StaffBenefitEntity getStaffBenefitInfo(String companyId, String staffId) {
        StaffBenefit staffBenefit = reposity.findByCompanyIdAndStaffId(companyId, staffId);
        if (staffBenefit == null) {
            return null;
        }
        StaffBenefitEntity result = new StaffBenefitEntity();
        result.setStaffId(staffId);
        result.setTmpSiBenefitId(staffBenefit.getTmpSiBenefitId());

        CompanyBenefit siCompanyBenefit = staffBenefit.getSiCompanyBenefit();
        if (siCompanyBenefit != null) {
            result.setSiBenefitId(siCompanyBenefit.getId());
        }
        BenefitEntity socialInsurance = staffBenefit.getSocialInsurance();
        if (socialInsurance != null) {
            result.setSiDivisionId(socialInsurance.getDivisionId());
            result.setSiBase(socialInsurance.getBase());
            result.setSiStartOn(socialInsurance.getStartOn());
            result.setSiEndOn(socialInsurance.getEndOn());
        }
        result.setSiAccountNumber(staffBenefit.getSiAccountNumber());

        CompanyBenefit hfCompanyBenefit = staffBenefit.getHfCompanyBenefit();
        if (hfCompanyBenefit != null) {
            result.setHfBenefitId(hfCompanyBenefit.getId());
        }
        BenefitEntity houseFund = staffBenefit.getHouseFund();
        if (houseFund != null) {
            result.setHfDivisionId(houseFund.getDivisionId());
            result.setHfBase(houseFund.getBase());
            result.setHfStartOn(houseFund.getStartOn());
            result.setHfEndOn(houseFund.getEndOn());
        }
        result.setHfAccountNumber(staffBenefit.getHfAccountNumber());
        result.setSupplementaryHfAccountNumber(staffBenefit.getSupplementaryHfAccountNumber());

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveStaffBenefitInfo(String companyId, StaffBenefitEntity entity) {
        String staffId = entity.getStaffId();
        ValidationUtils.rejectIfEmpty(staffId, null, "staffId数据不能为空");
        StaffBenefit staffBenefit = reposity.findByCompanyIdAndStaffId(companyId, staffId);
        //员工未设置过社保公积金数据
        if (staffBenefit == null) {
            staffBenefit = new StaffBenefit();
            SyncedBasicStaffInfo staff = new SyncedBasicStaffInfo();
            staff.setId(staffId);
            staffBenefit.setStaff(staff);
        }
        staffBenefit.setTmpSiBenefitId(entity.getTmpSiBenefitId());

        if (staffBenefit.getSocialInsurance() == null) {
            staffBenefit.setSocialInsurance(new BenefitEntity());
        }
        if (staffBenefit.getHouseFund() == null) {
            staffBenefit.setHouseFund(new BenefitEntity());
        }
        staffBenefit.getSocialInsurance().setBase(entity.getSiBase());
        staffBenefit.getSocialInsurance().setStartOn(entity.getSiStartOn());
        staffBenefit.getSocialInsurance().setEndOn(entity.getSiEndOn());
        staffBenefit.getSocialInsurance().setDivisionId(entity.getSiDivisionId());
        staffBenefit.setSiAccountNumber(entity.getSiAccountNumber());

        if (entity.getSiBenefitId() == null) {
            staffBenefit.setSiCompanyBenefit(null);
        } else {
            CompanyBenefit siBenefit = companyBenefitService.getOne(entity.getSiBenefitId());
            ValidationUtils.rejectIfEmpty(siBenefit, null, "社保方案无效");
            staffBenefit.setSiCompanyBenefit(siBenefit);
            //如果设置社保方案，则员工社保缴纳地必须与社保方案的缴纳地一致
            staffBenefit.getSocialInsurance().setDivisionId(siBenefit.getGroupDivisionId());
        }

        staffBenefit.getHouseFund().setBase(entity.getHfBase());
        staffBenefit.getHouseFund().setStartOn(entity.getHfStartOn());
        staffBenefit.getHouseFund().setEndOn(entity.getHfEndOn());
        staffBenefit.getHouseFund().setDivisionId(entity.getHfDivisionId());
        staffBenefit.setHfAccountNumber(entity.getHfAccountNumber());
        staffBenefit.setSupplementaryHfAccountNumber(entity.getSupplementaryHfAccountNumber());

        if (entity.getHfBenefitId() == null) {
            staffBenefit.setHfCompanyBenefit(null);
        } else {
            CompanyBenefit hfBenefit = companyBenefitService.getOne(entity.getHfBenefitId());
            ValidationUtils.rejectIfEmpty(hfBenefit, null, "公积金方案无效");
            staffBenefit.setHfCompanyBenefit(hfBenefit);
            //如果设置公积金方案，则员工公积金缴纳地必须与公积金方案的缴纳地一致
            staffBenefit.getHouseFund().setDivisionId(hfBenefit.getGroupDivisionId());
        }
        reposity.save(staffBenefit);
    }

    @Override
    public void saveStaffBenefitEndMonth(String companyId, StaffBenefitEntity entity) {
        String staffId = entity.getStaffId();
        ValidationUtils.rejectIfEmpty(staffId, null, "staffId数据不能为空");
        StaffBenefit staffBenefit = reposity.findByCompanyIdAndStaffId(companyId, staffId);
        //员工未设置过社保公积金数据
        if (staffBenefit == null) {
            staffBenefit = new StaffBenefit();
            SyncedBasicStaffInfo staff = new SyncedBasicStaffInfo();
            staff.setId(staffId);
            staffBenefit.setStaff(staff);
        }
        if (staffBenefit.getSocialInsurance() == null) {
            staffBenefit.setSocialInsurance(new BenefitEntity());
        }
        if (staffBenefit.getHouseFund() == null) {
            staffBenefit.setHouseFund(new BenefitEntity());
        }
        staffBenefit.getHouseFund().setEndOn(entity.getHfEndOn());
        staffBenefit.getSocialInsurance().setEndOn(entity.getSiEndOn());
        reposity.save(staffBenefit);
    }

    /**
     * 数据修复
     */
    @Override
    public void saveAllInsurance(String companyId, List<StaffBankSalaryEntity> staffBankSalaryEntityList) {
        //todo 保存所有
        if (CollectionUtils.isEmpty(staffBankSalaryEntityList)) {
            return;
        }
        List<SyncedBasicStaffInfo> staffInfoList = staffInfoRepository.findByCompanyIdAndStaffStatusNot(companyId, StaffStatus.DELETE);
        if (CollectionUtils.isEmpty(staffInfoList)) {
            return;
        }
        Map<String, SyncedBasicStaffInfo> staffMap = staffInfoList.stream().collect(Collectors.toMap(SyncedBasicStaffInfo::getId, Function.identity()));
        List<String> staffIdList = staffBankSalaryEntityList.stream().map(StaffBankSalaryEntity::getStaffId).collect(Collectors.toList());
        List<StaffBenefit> staffBenefitList = reposity.findAllByCompanyIdAndStaffIdIn(companyId, staffIdList);
        Map<String, StaffBenefit> benefitMap = staffBenefitList.stream().collect(Collectors.toMap(staffBenefit -> staffBenefit.getStaff().getId(), Function.identity()));
        List<StaffBenefit> syncBenefit = Lists.newArrayList();
        for (StaffBankSalaryEntity entity : staffBankSalaryEntityList) {
            if (!staffMap.containsKey(entity.getStaffId())) {
                continue;
            }
            SyncedBasicStaffInfo staff = staffMap.get(entity.getStaffId());

            StaffBenefit staffBenefit = benefitMap.get(entity.getStaffId());
            if (staffBenefit == null) {
                staffBenefit = new StaffBenefit();
                staffBenefit.setStaff(staff);
            }
            staffBenefit.setSiAccountNumber(entity.getSiAccountNumber());//社保账号
            staffBenefit.setTmpSiBenefitId(entity.getTmpSiBenefitId());//冗余字段：社保方案id
            //社保部分
            BenefitEntity social = staffBenefit.getSocialInsurance();
            if (social == null) {
                social = new BenefitEntity();
            }
            social.setBase(entity.getSiBase());//社保基数
            social.setStartOn(entity.getSiStartOn());//起始月份
            social.setEndOn(entity.getSiEndOn());//停缴月份
            social.setDivisionId(entity.getSiDivisionId());//缴纳地
            staffBenefit.setSocialInsurance(social);
            //公积金部分
            staffBenefit.setHfAccountNumber(entity.getHfAccountNumber());//公积金账号
            staffBenefit.setSupplementaryHfAccountNumber(entity.getSupplementaryHfAccountNumber());//补充公积金账号
            BenefitEntity houseFund = staffBenefit.getHouseFund();
            if (houseFund == null) {
                houseFund = new BenefitEntity();
            }
            houseFund.setBase(entity.getHfBase());//社保基数
            houseFund.setStartOn(entity.getHfStartOn());//起始月份
            houseFund.setEndOn(entity.getHfEndOn());//停缴月份
            houseFund.setDivisionId(entity.getHfDivisionId());//缴纳地
            staffBenefit.setHouseFund(houseFund);

            syncBenefit.add(staffBenefit);
        }
        reposity.save(syncBenefit);
    }
}
